package com.web;


import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.handler.WriteHandler;
import com.fasterxml.jackson.core.type.TypeReference;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * @author：zhang guoxiang
 * @date：2022/4/18
 */
public abstract class ExcelUtils {

    /**
     * @param file   文件
     * @param aClass 返回数据class
     */
    public static <T> List<T> read(File file, Class<T> aClass) {
        DataListener<T> dataListener = new DataListener<>();
        EasyExcelFactory.read(file.getAbsolutePath(), aClass, dataListener).sheet().doRead();
        return dataListener.getData();
    }

    /**
     * @param file      文件
     * @param reference 转换返回类型
     */
    public static <T> T read(File file, TypeReference<T> reference) {
        DataListener<T> dataListener = new DataListener<>();
        EasyExcelFactory.read(file.getAbsolutePath(), Object.class, dataListener).sheet().doRead();
        return JsonUtils.readValue(JsonUtils.toJsonStr(dataListener.getData()), reference);
    }

    /**
     * @param file    文件
     * @param aClass  返回数据class
     * @param sheetNo 读取第几个sheet 不填写默认0
     */
    public static <T> List<T> read(File file, Class<T> aClass, Integer sheetNo) {
        DataListener<T> dataListener = new DataListener<>();
        ExcelReader excelReader = null;
        try {
            excelReader = EasyExcelFactory.read(file.getAbsolutePath(), aClass, dataListener).build();
            // 构建一个sheet 这里可以指定名字或者no
            ReadSheet readSheet = EasyExcelFactory.readSheet(sheetNo == null ? 0 : sheetNo).build();
            // 读取一个sheet
            excelReader.read(readSheet);
        } finally {
            if (excelReader != null) {
                // 这里千万别忘记关闭，读的时候会创建临时文件，到时磁盘会崩的
                excelReader.finish();
            }
        }
        return dataListener.getData();
    }

    /**
     * 读取多个sheet
     * 多个表格内容格式必须一样
     *
     * @param file         文件
     * @param aClass       返回数据class
     * @param startSheetNo 从第几个sheet开始读 不填写默认0
     * @param endSheetNo   到第几个sheet停止读 不填写默认0
     */
    public static <T> List<T> repeatedRead(File file, Class<T> aClass, Integer startSheetNo, Integer endSheetNo) {
        DataListener<T> dataListener = new DataListener<>();
        startSheetNo = startSheetNo == null ? 0 : startSheetNo;
        endSheetNo = endSheetNo == null ? 0 : endSheetNo;
        if (startSheetNo > endSheetNo) {
            return Collections.emptyList();
        }
        ExcelReader excelReader = null;
        try {
            excelReader = EasyExcelFactory.read(file.getAbsolutePath(), aClass, dataListener).build();
            List<ReadSheet> list = new ArrayList<>();
            for (int i = startSheetNo; i <= endSheetNo; i++) {
                ReadSheet readSheet = EasyExcelFactory.readSheet(i).build();
                list.add(readSheet);
            }
            // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener

            // 这里注意 一定要把sheet1 sheet2 一起传进去，不然有个问题就是03版的excel 会读取多次，浪费性能
            excelReader.read(list);
        } finally {
            if (excelReader != null) {
                // 这里千万别忘记关闭，读的时候会创建临时文件，到时磁盘会崩的
                excelReader.finish();
            }
        }
        return dataListener.getData();
    }

    /**
     * @param inputStream 文件
     * @param head        返回数据class
     */
    public static <T> List<T> read(InputStream inputStream, Class<T> head) {
        DataListener<T> dataListener = new DataListener<>();
        EasyExcelFactory.read(inputStream, head, dataListener).sheet().doRead();
        return dataListener.getData();
    }


    /**
     * 无模板写文件
     *
     * @param filePath  输出文件路径
     * @param head      表头数据
     * @param data      表内容数据
     * @param sheetNo   sheet页号，从0开始
     * @param sheetName sheet名称
     */
    public static void write(String filePath, List<List<String>> head, List<List<Object>> data, Integer sheetNo, String sheetName) {
        EasyExcelFactory.write(filePath).head(head).sheet(sheetNo, sheetName).doWrite(data);
    }

    /**
     * 按模板写文件
     *
     * @param filePath  输出文件路径
     * @param headClazz 表头模板
     * @param data      数据
     * @param sheetNo   sheet页号，从0开始
     * @param sheetName sheet名称
     */
    public static void write(String filePath, Class<?> headClazz, List data, Integer sheetNo, String sheetName) {
        EasyExcelFactory.write(filePath, headClazz).sheet(sheetNo, sheetName).doWrite(data);
    }

    /**
     * 按模板写文件
     *
     * @param filePath     输出文件路径
     * @param headClazz    表头模板
     * @param data         数据
     * @param writeHandler 自定义的处理器，比如设置table样式，设置超链接、单元格下拉框等等功能都可以通过这个实现（需要注册多个则自己通过链式去调用）
     * @param sheetNo      sheet页号，从0开始
     * @param sheetName    sheet名称
     */
    public static void write(String filePath, Class<?> headClazz, List data, WriteHandler writeHandler, Integer sheetNo, String sheetName) {
        EasyExcelFactory.write(filePath, headClazz).registerWriteHandler(writeHandler).sheet(sheetNo, sheetName).doWrite(data);
    }

    /**
     * 按模板写文件（包含某些字段）
     *
     * @param filePath    输出文件路径
     * @param headClazz   表头模板
     * @param data        数据
     * @param includeCols 过滤包含的字段，根据字段名称过滤
     * @param sheetNo     sheet页号，从0开始
     * @param sheetName   sheet名称
     */
    public static void writeInclude(String filePath, Class<?> headClazz, List data, Set<String> includeCols, Integer sheetNo, String sheetName) {
        EasyExcelFactory.write(filePath, headClazz).includeColumnFiledNames(includeCols).sheet(sheetNo, sheetName).doWrite(data);
    }

    /**
     * 按模板写文件（排除某些字段）
     *
     * @param filePath    输出文件路径
     * @param headClazz   表头模板
     * @param data        数据
     * @param excludeCols 过滤排除的字段，根据字段名称过滤
     * @param sheetNo     sheet页号，从0开始
     * @param sheetName   sheet名称
     */
    public static void writeExclude(String filePath, Class<?> headClazz, List data, Set<String> excludeCols, Integer sheetNo, String sheetName) {
        EasyExcelFactory.write(filePath, headClazz).excludeColumnFiledNames(excludeCols).sheet(sheetNo, sheetName).doWrite(data);
    }

    /**
     * web页面导出
     *
     * @param response       数据返回
     * @param sheetName      sheet名称
     * @param exportFileName 导出文件名称
     * @param headList       表格头数据
     * @param dataList       表格数据
     */
    public static void webWrite(HttpServletResponse response, String sheetName, String exportFileName,
                                List<List<String>> headList, List<List<Object>> dataList) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        String fileName = URLEncoder.encode(exportFileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");

        EasyExcelFactory.write(response.getOutputStream())
                .autoCloseStream(true)
                .head(headList)
                .sheet(sheetName)
                .doWrite(dataList);
    }
}
